#ifndef _M_EXCHANGE_H_
#define _M_EXCHANGE_H_

#include <iostream>
#include <google/protobuf/map.h>
#include <string>
#include <vector>
#include <cassert>
#include <sstream>
#include <functional>
#include <mutex>

#include "../mqcommon/mqProto.pb.h"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/helper.hpp"


namespace MyRabbitMQ
{
    // 交换机数据类
    class Exchange
    {   
    public:
        using ptr = std::shared_ptr<Exchange>;
        Exchange() {} // 无参构造函数,用于测试

        //禁止拷贝构造与赋值运算符重载
        Exchange(const Exchange&) = delete; 
        Exchange& operator=(const Exchange&) = delete;
        //允许移动构造
        Exchange(Exchange&&) = default;

        // 构造函数
        Exchange(const std::string &name,
                 protoMQ::ExchangeType type,
                 bool isDurable, bool isAutoDelete,
                 const google::protobuf::Map<std::string, std::string> &args)
            : m_name(name), m_type(type), m_isDurable(isDurable), m_isAutoDelete(isAutoDelete), m_args(args)
        {}

        // 获取额外参数
        std::string getArgs()
        {
            std::string str;
            for (auto &f : m_args)
            {
                str += f.first + "=" + f.second + "&";
            }
            return str;
        }

        // 设置额外参数值
        void setArgs(const std::string &str)
        {
            std::vector<std::string> vt;
            StringHelper::split(str, "&", &vt);

            for (auto &f : vt)
            {
                size_t pos = f.find("=");
                std::string key = f.substr(0, pos);
                std::string value = f.substr(pos + 1);
                m_args[key] = value;
            }
        }

    public:
        std::string m_name;           // 交换机名称
        protoMQ::ExchangeType m_type; // 交换机类型
        bool m_isDurable;                     // 是否持久化

        // 不再使用的扩展字段
        bool m_isAutoDelete;                                    // 是否自动删除
        google::protobuf::Map<std::string, std::string> m_args; // 其他参数
    };

    //交换机数据内存管理结构
    using ExchangeMap = std::unordered_map<std::string,Exchange::ptr>;

    class ExchangeMapper
    {
    public:
        using ptr = std::shared_ptr<ExchangeMapper>;       
        //创建数据库文件的父级目录,保证父目录存在, 然后创建并打开数据库文件, 并调用创建表函数
        ExchangeMapper(const std::string& dbfilename)
        :m_sqlite(dbfilename)
        {
            //创建父目录
            std::string parentDir = FileHelper::parentDirectory(dbfilename);
            FileHelper::createDirectory(parentDir);

            //打开数据库文件,如果不存在则断言
            assert(m_sqlite.open());

            //创建表
            this->createTable();
        }

        //创建表
        bool createTable()
        {
            std::string sql = "create table if not exists exchange_table (\
            name varchar(32) primary key, type int, durable int, auto_delete int, args varchar(128));";

            bool ret = m_sqlite.exec(sql,nullptr,nullptr);
            
            //如果表都创建失败了,那就不要再继续进行下去了
            if(!ret)
            {
                LOG(logLevel::ERROR) << "创建交换机数据表失败! 程序即将退出" << std::endl;
                //直接异常退出程序
                abort();
            }

            return true;
        }

        //删除表
        bool removeTable()
        {
            std::string sql = "drop table if exists exchange_table;";
            
            bool ret = m_sqlite.exec(sql,nullptr,nullptr);

            if(!ret)
            {
                LOG(logLevel::ERROR) << "删除交换机数据表失败! 程序即将退出" << std::endl;
                //直接异常退出程序
                abort();
            }
            return true;
        }

        //新增交换机
        bool insert(const Exchange::ptr& ptr)
        {
            std::stringstream ss;

            ss << "insert into exchange_table values(";
            ss << "'" << ptr->m_name << "'" << ",";
            ss << ptr->m_type << ",";
            ss << ptr->m_isDurable << ",";
            ss << ptr->m_isAutoDelete << ",";
            ss << "'" << ptr->getArgs() << "'" << ");";

            return m_sqlite.exec(ss.str(),nullptr,nullptr);
        }

        //删除交换机
        bool remove(const std::string& name)
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name = '" <<  name << "';";
            return m_sqlite.exec(ss.str(),nullptr,nullptr);       
        }                                                         

        //获取所有交换机,进行恢复数据操作                                                        
        ExchangeMap recovery()                              
        {                                                         
            ExchangeMap result;                                   
            std::stringstream ss;                                 
            ss << "select * from exchange_table;";                
            m_sqlite.exec(ss.str(),recoverCallback,&result);
            return result;                                         
        }                                                        
                                                                 
    private:        
        SqliteHelper m_sqlite;

        //存入位置, 一行数据的列数, 一行数据的字符指针数组, 每列字段名称
        static int recoverCallback(void *args, int row, char ** dataOfCol, char **rowOfName)
        {
            ExchangeMap* result = reinterpret_cast<ExchangeMap*>(args);
            auto exchange_ptr = std::make_shared<Exchange>();

            exchange_ptr->m_name = dataOfCol[0];

            //原因: 在这个场景中，不能用 reinterpret_cast 替代 static_cast，因为 reinterpret_cast 不支持 int 到枚举类型的转换。
            //exchange_ptr->m_exchangeType = reinterpret_cast<protoMQ::ExchangeType>(std::stoi(dataOfCol[1]));
            //exchange_ptr->m_exchangeType = (protoMQ::ExchangeType)std::stoi(dataOfCol[1]);
            exchange_ptr->m_type = static_cast<protoMQ::ExchangeType>(std::stoi(dataOfCol[1]));
            exchange_ptr->m_isDurable = static_cast<bool>(std::stoi(dataOfCol[2]));
            exchange_ptr->m_isAutoDelete = static_cast<bool>(std::stoi(dataOfCol[3]));
            
            //如果存在args
            if(dataOfCol[4])
            {
                exchange_ptr->setArgs(dataOfCol[4]);
            }

            result->insert(std::make_pair(exchange_ptr->m_name,exchange_ptr));
            return 0;
        }
    };
    
    //交换机数据管理类
    class ExchangeManager
    {
    public:
        using ptr = std::shared_ptr<ExchangeManager>;
        
        ExchangeManager(const std::string& dbfilename)
        :m_mapper(dbfilename)
        {
            //恢复数据
            m_exchanges = m_mapper.recovery();
        }

        //声明交换机
        bool declareExchange(const std::string &name, protoMQ::ExchangeType type, bool isDurable, bool isAutoDelete, const google::protobuf::Map<std::string, std::string> &args)
        {
            //加锁
            std::unique_lock<std::mutex>(m_mtx);
            
            //判断交换机是否存在, 存在则直接返回
            if(m_exchanges.find(name) != m_exchanges.end()) 
            {
                return true;
            }

            //交换机不存在, 进行添加逻辑
            auto ptr = std::make_shared<Exchange>(name,type,isDurable,isAutoDelete,args);
           
            //如果设置持久化标志, 需要添加到数据库中, 由于该操作可能失败, 故放在内存层面添加之前
            if(isDurable)
            {
                bool ret = m_mapper.insert(ptr);                        
                if(!ret)
                {
                    LOG(logLevel::ERROR) << "执行失败: 进行持久化出现问题" << std::endl;
                    return false;
                }
            }
            //添加到内存中
            m_exchanges.insert(std::make_pair(name,ptr));     
            return true;   
        }

        //删除交换机
        bool deleteExchange(const std::string& name)
        {
            //加锁
            std::unique_lock<std::mutex>(m_mtx);

            //寻找内存中的交换机
            auto it = m_exchanges.find(name);
            
            if(it == m_exchanges.end())
            {
                return true;
            }

            //如果设置持久化标志, 需要先解除管理
            if(it->second->m_isDurable) 
            {
                bool ret = m_mapper.remove(name);
                if(!ret)
                {
                    LOG(logLevel::ERROR) << "执行失败: 进行持久化出现问题" << std::endl;
                    return false;
                }
            }

            //解除内存管理
            m_exchanges.erase(it);  
            return true;      
        }
        
        //获取指定交换机
        Exchange::ptr getExchange(const std::string& name)
        {
            std::unique_lock<std::mutex>(m_mtx);

            auto it = m_exchanges.find(name);
            if(it == m_exchanges.end())
            {
                return Exchange::ptr();
            }

            return it->second;            
        }

        //获取所有交换机
        ExchangeMap getExchangeAll()
        {
            std::unique_lock<std::mutex>(m_mtx);
            return m_exchanges;
        }

        //判断指定交换机是否存在
        bool exists(const std::string& name)
        {
            std::unique_lock<std::mutex>(m_mtx);

            auto it = m_exchanges.find(name);
            if(it == m_exchanges.end()) return false;
            else return true;            
        }

        //获取交换机管理数量
        size_t size()
        {
            std::unique_lock<std::mutex>(m_mtx); 
            return m_exchanges.size();
        }

        //清理所有交换机
        void clear()
        {
            std::unique_lock<std::mutex>(m_mtx);
            m_mapper.removeTable(); //删表等于变相清理数据
            m_exchanges.clear();
        }

    private:
        //锁
        std::mutex m_mtx;
        //交换机数据持久化管理句柄
        ExchangeMapper m_mapper;    
        //交换机数据内存层面管理句柄
        ExchangeMap m_exchanges;
    };
};

#endif